package org.dromara.business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.dromara.business.domain.*;
import org.dromara.business.domain.vo.*;
import org.dromara.business.mapper.*;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FFMpegUtils;
import org.dromara.common.core.utils.file.VideoFileInfo;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysOss;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
import org.springframework.stereotype.Service;
import org.dromara.business.domain.bo.VideoBo;
import org.dromara.business.service.IVideoService;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 视频基础信息Service业务层处理
 *
 * @author Hao Tong Xue
 * @date 2025-04-21
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class VideoServiceImpl implements IVideoService {

    private final VideoMapper baseMapper;

    private final UserFollowMapper userFollowMapper;

    private final ISysOssService ossService;

    private final ContentInteractionMapper contentInteractionMapper;

    private final ContentCollectionMapper collectionMapper;

    private final ContentCategoryMapper categoryMapper;

    private final VideoDanmakuMapper danmakuMapper;

    private final PlayHistoryMapper playHistoryMapper;

    private final ContentCommentMapper contentCommentMapper;

    private final ContentCommentLikesMapper contentCommentLikesMapper;

    /**
     * 查询视频基础信息
     *
     * @param id 主键
     * @return 视频基础信息
     */
    @Override
    public VideoVo queryById(Long id){

        VideoVo videoVo = baseMapper.selectVoById(id);
        if (videoVo != null) {

            videoVo.setLikeStatus(0);
            videoVo.setCoinStatus(0);

            List<ContentInteraction> contentInteractions = contentInteractionMapper.selectList(
                Wrappers.<ContentInteraction>lambdaQuery()
                    .eq(ContentInteraction::getContentId, id)
                    .eq(ContentInteraction::getContentType, 1)
                    .eq(BaseEntity::getCreateBy, LoginHelper.getUserId())
            );

            if (CollUtil.isNotEmpty(contentInteractions)) {

                if (contentInteractions.stream().anyMatch(s->s.getActionType() == 1)) {
                    videoVo.setLikeStatus(1);
                }

                videoVo.setCoinStatus(contentInteractions.stream().filter(s->s.getActionType() == 2).mapToInt(ContentInteraction::getActionValue).sum());

            }
        }

        return videoVo;
    }

    /**
     * 分页查询视频基础信息列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 视频基础信息分页列表
     */
    @Override
    public TableDataInfo<VideoVo> queryPageList(VideoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Video> lqw = buildQueryWrapper(bo);
        Page<VideoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public TableDataInfo<VideoVo> queryAdminPageList(VideoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<Video> lqw = buildQueryWrapper(bo);
        Page<VideoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);

        List<VideoVo> records = result.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<ContentCategoryVo> contentCategoryVos = categoryMapper.selectVoByIds(records.stream().map(VideoVo::getCategoryId).toList());
            List<ContentCollectionVo> contentCollectionVos = collectionMapper.selectVoByIds(records.stream().map(VideoVo::getCollectionId).toList());
            Map<Long, ContentCategoryVo> categoryMap = contentCategoryVos.stream().collect(Collectors.toMap(ContentCategoryVo::getId, s -> s));
            Map<Long, ContentCollectionVo> collectionMap = contentCollectionVos.stream().collect(Collectors.toMap(ContentCollectionVo::getId, s -> s));
            records.forEach(s->{
                s.setCategoryName(categoryMap.get(s.getCategoryId()).getCategoryName());
                s.setCollectionName(collectionMap.get(s.getCollectionId()).getTitle());
            });
        }

        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的视频基础信息列表
     *
     * @param bo 查询条件
     * @return 视频基础信息列表
     */
    @Override
    public List<VideoVo> queryList(VideoBo bo) {
        LambdaQueryWrapper<Video> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<Video> buildQueryWrapper(VideoBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<Video> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(Video::getId);
        lqw.likeRight(StringUtils.isNotBlank(bo.getTitle()), Video::getTitle, bo.getTitle());
        lqw.eq(StringUtils.isNotBlank(bo.getDescription()), Video::getDescription, bo.getDescription());
        lqw.likeRight(StringUtils.isNotBlank(bo.getResolution()), Video::getResolution, bo.getResolution());
        lqw.eq(StringUtils.isNotBlank(bo.getVideoFormat()), Video::getVideoFormat, bo.getVideoFormat());
        lqw.eq(bo.getSize() != null, Video::getSize, bo.getSize());
        lqw.eq(bo.getCollectionId() != null, Video::getCollectionId, bo.getCollectionId());
        lqw.eq(bo.getCategoryId() != null, Video::getCategoryId, bo.getCategoryId());
        lqw.eq(bo.getDuration() != null, Video::getDuration, bo.getDuration());
        lqw.eq(bo.getVideoType() != null, Video::getVideoType, bo.getVideoType());
        lqw.eq(StringUtils.isNotBlank(bo.getSourceInfo()), Video::getSourceInfo, bo.getSourceInfo());
        lqw.eq(bo.getIsRemix() != null, Video::getIsRemix, bo.getIsRemix());
        lqw.eq(bo.getVisibility() != null, Video::getVisibility, bo.getVisibility());
        lqw.eq(StringUtils.isNotBlank(bo.getCopyrightInfo()), Video::getCopyrightInfo, bo.getCopyrightInfo());
        lqw.eq(bo.getDisableDanmaku() != null, Video::getDisableDanmaku, bo.getDisableDanmaku());
        lqw.eq(bo.getDisableComment() != null, Video::getDisableComment, bo.getDisableComment());
        lqw.eq(bo.getEnableFeaturedComments() != null, Video::getEnableFeaturedComments, bo.getEnableFeaturedComments());
        lqw.eq(bo.getVideoStatus() != null, Video::getVideoStatus, bo.getVideoStatus());
        lqw.ge(bo.getPlayCount() != null, Video::getPlayCount, bo.getPlayCount());
        lqw.ge(bo.getDanmakuCount() != null, Video::getDanmakuCount, bo.getDanmakuCount());
        lqw.ge(bo.getCommentCount() != null, Video::getCommentCount, bo.getCommentCount());
        lqw.ge(bo.getLikeCount() != null, Video::getLikeCount, bo.getLikeCount());
        lqw.ge(bo.getCoinCount() != null, Video::getCoinCount, bo.getCoinCount());
        lqw.ge(bo.getFavoriteCount() != null, Video::getFavoriteCount, bo.getFavoriteCount());
        lqw.ge(bo.getShareCount() != null, Video::getShareCount, bo.getShareCount());
        lqw.eq(bo.getCreateBy() != null, BaseEntity::getCreateBy, bo.getCreateBy());
        return lqw;
    }

    /**
     * 新增视频基础信息
     *
     * @param bo 视频基础信息
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(List<VideoBo> bo) {
        List<Video> videoList = MapstructUtils.convert(bo, Video.class);

        if (CollUtil.isEmpty(videoList)) {
            return false;
        }

        List<SysOssVo> fileList = ossService.getFileByIds(videoList.stream().map(Video::getFileId).toList());
        Map<Long, String> fileMap = fileList.stream().collect(Collectors.toMap(SysOssVo::getOssId, SysOssVo::getFilePath));


        for (Video video : videoList) {

            String videoPath = fileMap.get(video.getFileId());
            VideoFileInfo videoInfo = FFMpegUtils.getVideoInfo(videoPath);
            if (videoInfo == null) {
                throw new ServiceException();
            }

            try {
                String fileStoragePath = ossService.getFileStoragePath(".jpg");
                JSONObject jsonObject = FFMpegUtils.generateSprite(videoPath, videoInfo.duration, videoInfo.width, videoInfo.height, fileStoragePath);
                SysOssVo spriteOssVo = ossService.upload(new File(fileStoragePath));

                JSONArray urls = new JSONArray(1);
                urls.add(spriteOssVo.getUrl());
                jsonObject.put("urls", urls);
                // 封面图配置
                String thumbnailInfo = jsonObject.toString();
                video.setSpriteId(spriteOssVo.getOssId());
                video.setThumbnailInfo(thumbnailInfo);

            } catch (Exception e) {
                log.error("生成精灵图失败", e);
            }

            video.setResolution(videoInfo.resolutionLabel);
            video.setVideoFormat(videoInfo.formatName);
            video.setDuration(Convert.toStr(videoInfo.duration));

        }
        return baseMapper.insertBatch(videoList);
    }

    /**
     * 修改视频基础信息
     *
     * @param bo 视频基础信息
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(VideoBo bo) {
        Video update = MapstructUtils.convert(bo, Video.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Video entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除视频基础信息信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }

        playHistoryMapper.delete(Wrappers.<PlayHistory>lambdaQuery().in(PlayHistory::getContentId, ids));
        danmakuMapper.delete(Wrappers.<VideoDanmaku>lambdaQuery().in(VideoDanmaku::getVideoId, ids));
        contentInteractionMapper.delete(Wrappers.<ContentInteraction>lambdaQuery().in(ContentInteraction::getContentId, ids));
        LambdaQueryWrapper<ContentComment> commentLambdaQueryWrapper = Wrappers.<ContentComment>lambdaQuery().in(ContentComment::getContentId, ids);
        List<ContentCommentVo> contentCommentVos = contentCommentMapper.selectVoList(commentLambdaQueryWrapper);
        if (CollUtil.isNotEmpty(contentCommentVos)) {
            contentCommentMapper.delete(commentLambdaQueryWrapper);
            contentCommentLikesMapper.delete(Wrappers.<ContentCommentLikes>lambdaQuery().in(ContentCommentLikes::getCommentId, contentCommentVos.stream().map(ContentCommentVo::getId).toList()));
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    /**
     * 视频数据
     * @return 统计概率
     */
    @Override
    public VideoDataVo videoData() {
        VideoDataVo videoData = baseMapper.videoData(LoginHelper.getUserId());
        videoData.setFollowingCount(userFollowMapper.selectCount(Wrappers.<UserFollow>lambdaQuery().eq(UserFollow::getFollowingId, LoginHelper.getUserId())));
        return videoData;
    }

    /**
     * 交互操作
     * @param bo  -> operateType 1-点赞 2-投币 3-收藏 4-分享
     */
    @Override
    public void interactiveOperate(VideoBo bo) {

        ContentInteraction contentInteraction = new ContentInteraction();
        contentInteraction.setContentId(bo.getId());
        contentInteraction.setContentType(1);
        contentInteraction.setActionType(bo.getOperateType());
        contentInteraction.setActionTime(new Date());

        Video video = baseMapper.selectById(bo.getId());
        List<ContentInteraction> contentInteractions = contentInteractionMapper.selectList(
            Wrappers.<ContentInteraction>lambdaQuery()
                .eq(ContentInteraction::getContentId, bo.getId())
                .eq(ContentInteraction::getContentType, 1)
                .eq(BaseEntity::getCreateBy, LoginHelper.getUserId())
        );
        switch (bo.getOperateType()) {

            case 1:
                if (contentInteractions.stream().anyMatch(s->s.getActionType() == 1)) {
                    video.setLikeCount(video.getLikeCount() - 1);
                    contentInteractionMapper.delete(
                        Wrappers.<ContentInteraction>lambdaQuery()
                            .eq(ContentInteraction::getContentId, bo.getId())
                            .eq(ContentInteraction::getActionType, 1)
                            .eq(BaseEntity::getCreateBy, LoginHelper.getUserId())
                    );
                }else {
                    video.setLikeCount(video.getLikeCount() + 1);
                    contentInteraction.setActionValue(1);
                    contentInteractionMapper.insert(contentInteraction);
                }
                break;
            case 2:

                if (bo.getCoinWhitLikeStatus() == 0 && contentInteractions.stream().noneMatch(s->s.getActionType() == 1)) {
                    ContentInteraction likeContentInteraction = new ContentInteraction();
                    likeContentInteraction.setContentId(bo.getId());
                    likeContentInteraction.setContentType(1);
                    likeContentInteraction.setActionType(1);
                    likeContentInteraction.setActionTime(new Date());
                    likeContentInteraction.setActionValue(1);
                    contentInteractionMapper.insert(likeContentInteraction);

                    video.setLikeCount(video.getLikeCount() + 1);
                }

                int actionCoinTotal = contentInteractions.stream().filter(s -> s.getActionType() == 2).mapToInt(ContentInteraction::getActionValue).sum();
                if (actionCoinTotal >= 2) {
                    throw new ServiceException("对本稿件的投币枚数已用完");
                }
                contentInteraction.setCoinType(bo.getCoinCount()); // 1-普通 2-双倍
                contentInteraction.setActionValue(bo.getCoinCount());
                contentInteractionMapper.insert(contentInteraction);
                video.setCoinCount(video.getCoinCount() + bo.getCoinCount());
                break;
            case 3:

                break;
            case 4:
                break;

        }

        baseMapper.updateById(video);

    }

    /**
     * 获取用户最近观看的视频
     * @return 视频列表
     */
    @Override
    public Map<String, List<VideoVo>> getRecentLikeVideos(Long userId) {
        Date startDate = DateUtils.getDateWithOffset(-30);
        // 点赞10条
        Page<ContentInteraction> likePage = new PageQuery(10, 1).build();
        IPage<ContentInteractionVo> likePageResult = contentInteractionMapper.selectVoPage(
            likePage,
            Wrappers.<ContentInteraction>lambdaQuery()
                .eq(BaseEntity::getCreateBy, userId)
                .eq(ContentInteraction::getContentType, 1)
                .eq(ContentInteraction::getActionType, 1)
                .ge(BaseEntity::getCreateTime, startDate)
                .orderByDesc(BaseEntity::getCreateTime)
        );
        // 投币10条
        List<Long> coinIds = contentInteractionMapper.selectCoinInteractionVideoIds(userId, startDate);

        // 收集所有视频ID
        Set<Long> allVideoIds = new HashSet<>();
        List<Long> likeIds = likePageResult.getRecords().stream().map(ContentInteractionVo::getContentId).toList();
        allVideoIds.addAll(likeIds);
        allVideoIds.addAll(coinIds);
        if (allVideoIds.isEmpty()) {
            return Collections.emptyMap();
        }
        List<VideoVo> videoVos = baseMapper.selectVoByIds(new ArrayList<>(allVideoIds));
        Map<Long, VideoVo> idToVideo = videoVos.stream().collect(Collectors.toMap(VideoVo::getId, v -> v));
        // 构建结果
        Map<String, List<VideoVo>> result = new HashMap<>();
        List<VideoVo> likeList = likeIds.stream().map(idToVideo::get).filter(Objects::nonNull).collect(Collectors.toList());
        List<VideoVo> coinList = coinIds.stream().map(idToVideo::get).filter(Objects::nonNull).collect(Collectors.toList());
        if (!likeList.isEmpty()) result.put("like", likeList);
        if (!coinList.isEmpty()) result.put("coin", coinList);
        return result;
    }
}
